﻿using System;
using nRoute.ViewModels;
using nRoute.Components;
using nRoute.Samples.Sudoku.Model;
using nRoute.Samples.Sudoku.ViewServices;
using nRoute.ViewServices;
using nRoute.Samples.Sudoku.Components;
using nRoute.Services;
using nRoute.Samples.Sudoku.Services;
using nRoute.Components.Composition;

namespace nRoute.Samples.Sudoku.ViewModels
{
    public class GameViewModel 
        : ViewModelBase
    {

#region Declarations and Variables

        private const string COMPLETED_MSG = "Congratulations, you have successfully completed the Sudoku puzzle.";
        private const string NOT_COMPLETED_MSG = "Sorry, the Sudoku puzzle is not either fully or correctly completed.";
        private const string ERROR_MSG = "An error occurred: {0}";
        private const string FILE_NAME = "___nRoute.Samples.Sudoku";

        private Lazy<IBoxValueViewService> _boxValueViewService;
        private Lazy<IMessageViewService> _messageViewService;
        private Lazy<IWorkingIndicatorViewService> _indicatorViewService;

#endregion

        [ResolveConstructor]
        public GameViewModel(Lazy<IBoxValueViewService> boxValueViewService, Lazy<IMessageViewService> messageViewService, 
            Lazy<IWorkingIndicatorViewService> indicatorViewService)
        {
            _boxValueViewService = boxValueViewService;
            _messageViewService = messageViewService;
            _indicatorViewService = indicatorViewService;

            SetupCommands();
        }

#region Properties

        public Game Game { get; private set; }

        public ActionCommand<GameLevel> NewGameCommand { get; private set; }

        public ActionCommand<Box> SetBoxValueCommand { get; private set; }

        public ActionCommand ConfirmCommand { get; private set; }

        public ActionCommand HintCommand { get; private set; }

        public ActionCommand ResetCommand { get; private set; }

        public ActionCommand SolveCommand { get; private set; }

        public ActionCommand ViewBoardReverseCommand { get; private set; }

        public ActionCommand SaveGameCommand { get; private set; }

        public ActionCommand RestoreGameCommand { get; private set; }

#endregion

#region Helpers

        private void SetupCommands()
        {
            ViewBoardReverseCommand = new ActionCommand(() => { });
            
            NewGameCommand = new ActionCommand<GameLevel>((l) =>
            {
                // indicate working
                _indicatorViewService.Value.Show(this);

                // set up a new game
                var _generateGameService = ServiceLocator.GetService<IGenerateGameService>();
                _generateGameService.GenerateGame(l, (g) =>
                {
                     // save the game
                     Game = g;
                     NotifyPropertyChanged(() => Game);
                    
                     // and ensure we see the board visuals and hide the indicator
                     ViewBoardReverseCommand.Execute();
                     _indicatorViewService.Value.Hide(this);
                });
            });

            SetBoxValueCommand = new ActionCommand<Box>((b) =>
            {                            
                // we ask the view service to ask the user for the value, note it returns async'ly
                _boxValueViewService.Value.GetBoxValue((v)=>
                {
                    // ie. was cancelled
                    if (!v.HasValue) return;

                    // else we set the value in the box
                    b.SuggestedValue = v.Value;
                });
            });

            ConfirmCommand = new ActionCommand(() => ShowMessage(Game.VerifyGame() ? COMPLETED_MSG : NOT_COMPLETED_MSG), 
                () => Game != null);

            ResetCommand = new ActionCommand(() => Game.ResetBoard(), () => Game != null);

            HintCommand = new ActionCommand(() => Game.HintMove(), () => Game != null);

            SolveCommand = new ActionCommand(() => Game.SolveGame(), () => Game != null);

            SaveGameCommand = new ActionCommand(() =>
            {
                try 
                {
                    IsolatedStore.SaveDataObject(Game, FILE_NAME);
                    ViewBoardReverseCommand.Execute();
                }
                catch (Exception ex)
                {
                    ShowMessage(string.Format(ERROR_MSG, ex.Message));
                }

            }, () => Game != null).
            RequeryOnPropertyChanged(this, () => Game);

            RestoreGameCommand = new ActionCommand(() =>
            {
                if (IsolatedStore.FileExists(FILE_NAME))
                {
                    try
                    {
                        var _game = IsolatedStore.LoadDataObject<Game>(FILE_NAME);
                        if (_game != null)
                        {
                            this.Game = _game;
                            NotifyPropertyChanged(() => Game);
                        }
                        ViewBoardReverseCommand.Execute();
                    }
                    catch(Exception ex)
                    {
                        ShowMessage(string.Format(ERROR_MSG, ex.Message));
                    }
                }
            }, () => IsolatedStore.FileExists(FILE_NAME))
            .RequeryOnCommandExecuted(SaveGameCommand);
        }

        private void ShowMessage(string message)
        {
            // show the message
            _messageViewService.Value.ShowMessage(message);
        }

#endregion

    }
}